package com.jarveis.frame.util;

import java.io.FileInputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.log4j.Logger;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.tree.DefaultElement;

import com.jarveis.frame.io.IOUtil;
import com.jarveis.frame.lang.StringUtil;

/**
 * 请求对象
 * 
 * @author liuguojun
 */
public class Param {

	public static final String REQ = "Req"; // 请求
	public static final String RESP = "Resp"; // 响应
	public static final String HEAD = "head"; // 请求头部
	public static final String BODY = "body"; // 请求内容

	public static final String LABEL_ERROR = "@errcode"; // 错误标签
	public static final String LABEL_FUNCID = "@funcId"; // 方法标识标签
	public static final String LABEL_DATATYPE = "@dataType"; // 数据类型

	public static final String DT_XML = "xml"; // 数据类型
	public static final String DT_JSON = "json"; // 数据类型

	public static final String ERROR_SUCCESS = "0000"; // 操作成功
	public static final int ERROR_EXCEPTION = 9999; // 操作成功

	public static final char PERFIX_XML = '<'; // xml前缀
	public static final char PERFIX_JSON = '{'; // json前缀
	private static final Logger logger = Logger.getLogger(Param.class);

	private Element element;
	private String tagName;

	public Param(String str) throws Exception {
		char prefix = 0;
		str = (!StringUtil.isBlank(str)) ? str.trim() : REQ;
		if (str.length() > 0) {
			prefix = str.charAt(0);
		}

		if (prefix == PERFIX_XML) {
			parseDomStr(str);
		} else if (prefix == PERFIX_JSON) {
			parseJsonStr(str);
		} else if (REQ.equals(str) || RESP.equals(str)) {
			element = new DefaultElement(str);
			addParam(HEAD);
			addParam(BODY);
		} else {
			throw new Exception("构建数据包异常");
		}
		tagName = element.getName();

		checkBuilder();
	}

	/**
	 * 构造器
	 * 
	 * @param ele
	 */
	public Param(Element ele) {
		this.element = ele;
		this.tagName = this.element.getName();
	}

	/**
	 * 解析xml字符串
	 * 
	 * @param str
	 */
	private void parseDomStr(String str) {
		try {
			Document doc = DocumentHelper.parseText(str);
			element = doc.getRootElement();
		} catch (Exception ex) {
			element = new DefaultElement(str);
			logger.error(ex.getMessage(), ex);
		}
	}

	/**
	 * 解析json字符串
	 * 
	 * @param str
	 */
	private void parseJsonStr(String str) {
		try {
			Map map = (Map) JsonUtil.parse(str);

			element = new DefaultElement(REQ);
			this.setPropertys(map);
		} catch (Exception ex) {
			element = new DefaultElement(str);
			logger.error(ex.getMessage(), ex);
		}
	}

	/**
	 * 校验构建数据
	 */
	private void checkBuilder() {
		Map props = getPropertys();
		removePropertys();

		Param head = getHead();
		if (head == null) {
			head = addParam("head");
		}
		Param body = getBody();
		if (body == null) {
			body = addParam("body");
		}
		List<Param> childs = getChilds(null);
		for (Param child : childs) {
			String tagName = child.getTagName();
			if ((!"head".equals(tagName)) && (!"body".equals(tagName))) {
				removeChilds(tagName);

				body.addParam(child.getElement(false));
			}
		}
		body.setPropertys(props);
	}

	/**
	 * 添加属性
	 * 
	 * @param path
	 * @param value
	 */
	public void setProperty(String path, boolean value) {
		setProperty(path, String.valueOf(value));
	}

	/**
	 * 添加属性
	 * 
	 * @param path
	 * @param value
	 */
	public void setProperty(String path, Date date) {
		if (date != null) {
			SimpleDateFormat format = new SimpleDateFormat(
					"yyyy-MM-dd HH:mm:ss");
			setProperty(path, format.format(date));
		}
	}

	/**
	 * 添加属性
	 * 
	 * @param path
	 * @param value
	 */
	public void setProperty(String path, BigDecimal decimal) {
		if (decimal != null) {
			setProperty(path, decimal.toString());
		}
	}

	/**
	 * 添加属性
	 * 
	 * @param path
	 * @param value
	 */
	public void setProperty(String path, float value) {
		setProperty(path, String.valueOf(value));
	}

	/**
	 * 添加属性
	 * 
	 * @param path
	 * @param value
	 */
	public void setProperty(String path, double value) {
		setProperty(path, String.valueOf(value));
	}

	/**
	 * 添加属性
	 * 
	 * @param path
	 * @param value
	 */
	public void setProperty(String path, int value) {
		setProperty(path, String.valueOf(value));
	}

	/**
	 * 添加属性
	 * 
	 * @param path
	 * @param value
	 */
	public void setProperty(String path, long value) {
		setProperty(path, String.valueOf(value));
	}

	/**
	 * 添加属性
	 * 
	 * @param path
	 * @param value
	 */
	public void setProperty(String path, short value) {
		setProperty(path, String.valueOf(value));
	}

	/**
	 * 添加属性
	 * 
	 * @param path
	 * @param value
	 */
	public void setProperty(String path, Object[] value) {
		if (value != null) {
			StringBuilder buf = new StringBuilder();
			for (int i = 0; i < value.length; i++) {
				buf.append(value[i]);
				if (i < value.length - 1) {
					buf.append(",");
				}
			}
			setProperty(path, buf.toString());
		}
	}

	/**
	 * 添加属性
	 * 
	 * @param path
	 * @param value
	 */
	public void setProperty(String path, String value) {
		if (path != null && !"".equals(path)) {
			if (path.startsWith("@")) {
				path = path.substring(1);
				Attribute attr = element.attribute(path);
				if (attr != null) {
					attr.setValue(value);
				} else {
					element.addAttribute(path, value);
				}
			}
		}
	}

	/**
	 * 设置属性集
	 * 
	 * @return
	 */
	public void setPropertys(Map map) {
		Set<Entry<String, Object>> set = map.entrySet();

		Iterator it = set.iterator();
		while (it.hasNext()) {
			Entry entry = (Entry) it.next();
			String path = (String) entry.getKey();
			if (!path.startsWith("@")) {
				path = "@" + path;
			}
			Object value = entry.getValue();
			if (value instanceof String) {
				setProperty(path, (String) value);
			} else if (value instanceof Short) {
				setProperty(path, (Short) value);
			} else if (value instanceof Integer) {
				setProperty(path, (Integer) value);
			} else if (value instanceof Long) {
				setProperty(path, (Long) value);
			} else if (value instanceof Float) {
				setProperty(path, (Float) value);
			} else if (value instanceof Double) {
				setProperty(path, (Double) value);
			} else if (value instanceof Boolean) {
				setProperty(path, (Boolean) value);
			} else if (value instanceof Date) {
				setProperty(path, (Date) value);
			} else if (value instanceof String[]) {
				setProperty(path, (String[]) value);
			} else if (value instanceof Map) {
				Param param = this.addParam(path.substring(1));
				param.setPropertys((Map) value);
			} else if (value instanceof List) {
				List list = (List) value;
				for (Object obj : list) {
					Param param = this.addParam(path.substring(1));
					param.setPropertys((Map) obj);
				}
			}
		}
	}

	/**
	 * 删除属性
	 * 
	 * @param path
	 */
	public void removeProperty(String path) {
		if (path != null && path.startsWith("@")) {
			path = path.substring(1);
			Attribute attr = element.attribute(path);
			if (attr != null) {
				element.remove(attr);
			}
		}
	}

	/**
	 * 删除节点所有属性
	 */
	public void removePropertys() {
		Map props = this.getPropertys();
		Set keys = props.keySet();
		for (Object key : keys) {
			String path = "@" + (String) key;
			this.removeProperty(path);
		}

	}

	/**
	 * 添加CDATA
	 * 
	 * @param content
	 */
	public void addCDATA(String content) {
		if (content != null && !"".equals(content)) {
			element.addCDATA(content);
		}
	}

	/**
	 * 添加子节点
	 * 
	 * @param path
	 * @return
	 */
	public Param addParam(String path) {
		Param vo = null;

		if (path != null && !"".equals(path)) {
			Element ele = element.addElement(path);
			vo = new Param(ele);
		}

		return vo;
	}

	/**
	 * 添加子节点
	 * 
	 * @param ele
	 * @return Param
	 */
	public Param addParam(Element ele) {
		Param vo = null;

		if (ele != null) {
			element.add(ele);
			vo = new Param(ele);
		}

		return vo;
	}

	/**
	 * 获取属性值
	 * 
	 * @param path
	 * @return
	 */
	public BigDecimal getBigDecimal(String path) {
		return new BigDecimal(getProperty(path, "0"));
	}

	/**
	 * 获取属性值
	 * 
	 * @param path
	 * @return
	 */
	public Date getDate(String path) {
		Date date = null;
		try {
			String value = getProperty(path);

			String math1 = "\\d{1,4}-\\d{1,2}-\\d{1,2}";
			String math2 = "\\d{1,4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}";
			SimpleDateFormat format = null;
			if (value.matches(math1)) {
				format = new SimpleDateFormat("yyyy-MM-dd");
			} else if (value.matches(math2)) {
				format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			} else {
				throw new Exception(value + " 格式错误");
			}
			date = format.parse(value);
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		}

		return date;
	}

	/**
	 * 获取属性值
	 * 
	 * @param path
	 * @return
	 */
	public boolean getBoolean(String path) {
		return Boolean.parseBoolean(getProperty(path, "false"));
	}

	/**
	 * 获取属性值
	 * 
	 * @param path
	 * @return
	 */
	public double getDouble(String path) {
		return Double.parseDouble(getProperty(path, "0"));
	}

	/**
	 * 获取属性值
	 * 
	 * @param path
	 * @return
	 */
	public short getShort(String path) {
		return Short.parseShort(getProperty(path, "0"));
	}

	/**
	 * 获取属性值
	 * 
	 * @param path
	 * @return
	 */
	public int getInteger(String path) {
		return Integer.parseInt(getProperty(path, "0"));
	}

	/**
	 * 获取属性值
	 * 
	 * @param path
	 * @return
	 */
	public long getLong(String path) {
		return Long.parseLong(getProperty(path, "0"));
	}

	/**
	 * 获取属性值
	 * 
	 * @param path
	 * @return
	 */
	public String getString(String path) {
		return this.getProperty(path);
	}

	/**
	 * 获取属性值
	 * 
	 * @param path
	 * @return
	 */
	private String getProperty(String path) {
		String value = null;
		if (path.startsWith("@")) {
			path = path.substring(1);
			value = element.attributeValue(path);
		}

		if (value == null) {
			value = "";
		}

		return value;
	}

	/**
	 * 获取属性值， 如果不存在返回默认值
	 * 
	 * @param path
	 * @return
	 */
	private String getProperty(String path, String defvalue) {
		String value = getProperty(path);
		if (StringUtil.isEmpty(value.trim())) {
			value = defvalue;
		}

		return value;
	}

	/**
	 * 获取属性信息
	 * 
	 * @return
	 */
	public Map<String, String> getPropertys() {
		Map<String, String> map = null;

		List attrs = element.attributes();
		map = new HashMap<String, String>(attrs.size());
		for (int i = 0; i < attrs.size(); i++) {
			Attribute attr = (Attribute) attrs.get(i);
			map.put(attr.getName(), attr.getValue());
		}

		return map;
	}

	/**
	 * 获取CDATA值
	 * 
	 * @return
	 */
	public String getCDATA() {
		return element.getTextTrim();
	}

	/**
	 * 获取数据包头部
	 * 
	 * @return
	 */
	public Param getHead() {
		return getChild(HEAD);
	}

	/**
	 * 获取数据包主体
	 * 
	 * @return
	 */
	public Param getBody() {
		return getChild(BODY);
	}

	/**
	 * 通过节点名称删除单个节点
	 * 
	 * @param path
	 */
	public void removeChild(String path) {
		if (!StringUtil.isEmpty(path)) {
			Element temp = element.element(path);
			if (temp != null) {
				element.remove(temp);
			}
		}

	}

	/**
	 * 通过节点名称删除多个节点
	 * 
	 * @param path
	 */
	public void removeChilds(String path) {
		if (!StringUtil.isEmpty(path)) {
			List eleList = element.elements(path);
			for (int i = 0; i < eleList.size(); i++) {
				Element ele = (Element) eleList.get(i);
				element.remove(ele);
			}
		}
	}

	/**
	 * 获取子节点
	 * 
	 * @param path
	 * @return
	 */
	public Param getChild(String path) {
		Param vo = null;

		if (!StringUtil.isEmpty(path)) {
			Element temp = element.element(path);
			if (temp != null) {
				vo = new Param(temp);
			}
		} else {
			List list = element.elements();
			if (!list.isEmpty()) {
				Element temp = (Element) list.get(0);
				vo = new Param(temp);
			}
		}

		return vo;
	}

	/**
	 * 获取子节点列表
	 * 
	 * @param path
	 * @return
	 */
	public List<Param> getChilds(String path) {
		List<Param> voList = null;

		List eleList = null;
		if (!StringUtil.isEmpty(path)) {
			eleList = element.elements(path);
		} else {
			eleList = element.elements();
		}
		voList = new ArrayList<Param>(eleList.size());

		for (int i = 0; i < eleList.size(); i++) {
			Element ele = (Element) eleList.get(i);
			voList.add(new Param(ele));
		}

		return voList;
	}

	/**
	 * 获取标签名称
	 * 
	 * @return String
	 */
	public String getTagName() {
		return this.tagName;
	}

	/**
	 * 返回根节点
	 * 
	 * @return
	 */
	public Element getElement(boolean is) {
		Element ele = element;

		if (is) {
			while (ele.getParent() != null) {
				ele = ele.getParent();
			}
		} else {
			ele.setParent(null);
		}

		return ele;
	}

	/**
	 * 转换成Map对象
	 * 
	 * @return
	 */
	private Map toMap(Element e) {
		Map map = new HashMap();

		String text = e.getText();
		if (!StringUtil.isBlank(text)) {
			e.addAttribute("_text", StringUtil.trim(text));
		}
		
		List attrs = e.attributes();
		for (int i = 0; i < attrs.size(); i++) {
			Attribute attr = (Attribute) attrs.get(i);
			map.put(attr.getName(), attr.getValue());
		}

		List eles = e.elements();
		for (int i = 0; i < eles.size(); i++) {
			Element ele = (Element) eles.get(i);
			String key = ele.getName();
			Map value = toMap(ele);
			Object obj = map.get(key);
			if (obj != null) {
				if (obj instanceof Map) {
					List list = new ArrayList();
					list.add(obj);
					list.add(value);
					map.put(key, list);
				} else if (obj instanceof List) {
					List list = (List) obj;
					list.add(value);
					map.put(key, list);
				}
			} else {
				map.put(key, value);
			}
		}

		return map;
	}

	/**
	 * 转换成json字符串
	 * 
	 * @return
	 */
	public String toJsonString() {
		return JsonUtil.toJson(toMap(this.element));
	}

	public String toXmlString() {
		Element root = getElement(false);
		Document document = root.getDocument();
		if (document == null) {
			document = DocumentHelper.createDocument();
			document.setRootElement(root);
		}
		return document.asXML().replaceAll("\\n", "");
	}

	public String toString() {
		String dataType = this.getHead().getString(LABEL_DATATYPE);
		if (DT_JSON.equals(dataType)) {
			return toJsonString();
		} else {
			return toXmlString();
		}
	}

	public static void main(String[] args) throws Exception {
		String jsonStr = IOUtil.read(new FileInputStream("D:\\temp\\1.txt"));
		Param param = new Param(jsonStr);
		System.out.println(param.toJsonString());
	}
}
